# -*- coding: UTF-8 -*-
import serial#引入串口库
import time#引入延时库
import tkinter as tk
from tkinter import *#引入UI库
import threading#引入定时回调库
from socket import *#引入网络库
#参数定义
LCD_X = 1300#显示宽度
PAG_NUM = 8#缓存页数
SP_NUM = 20#空格数
Show_H = 520#画布高度
#全局变量
PAG_USE = 0 #最新页
LEN_USE = 0 #最新行
HZ_NOW = 0  #最新累计
HZ_OLD = 0  #上一秒累计
HZ_Show = 0  #上一显示累计
Show_Pag = 0 #当前显示页
Show_Len = 0 #当前显示行

Time_USE = 2 #采样显示时间间隔(单位ms)
Range_USE = 0.1 #采样显示电压范围（放大倍数）
ADC_Data=[[[0 for o in range(4)] for o in range(LCD_X)]for o in range(PAG_NUM)]#创建三维数组[通道][数值][分组]
Show_Data=[[0 for o in range(4)] for o in range(LCD_X)]#创建二维数组,用于显示
line0_Data=[[0 for o in range(2)] for o in range(LCD_X)]#创建二维数组,用于显示
line1_Data=[[0 for o in range(2)] for o in range(LCD_X)]#创建二维数组,用于显示
line2_Data=[[0 for o in range(2)] for o in range(LCD_X)]#创建二维数组,用于显示
line3_Data=[[0 for o in range(2)] for o in range(LCD_X)]#创建二维数组,用于显示
line0_color='Red'
line1_color='Blue'
line2_color='Green'
line3_color='Black'

for value0 in range(LCD_X):
	line0_Data[value0][0]=value0
	line1_Data[value0][0]=value0
	line2_Data[value0][0]=value0
	line3_Data[value0][0]=value0
#print(line0_Data)

ser = serial.Serial("COM15",100000,timeout = 5)#初始化串口连接

#按键事件
def button1():#创建连接
	global timer1#声明为全局变量
	global timer2
	global timer3
	global ser
	ser.flushInput()#开启串口接收
	timer1=threading.Timer(0.2,TIM1)#0.2秒之后触发
	timer1.start()
	timer2=threading.Timer(0.2,TIM2)#0.2秒之后触发
	timer2.start()
	timer3=threading.Timer(0.2,TIM3)#0.2秒之后触发
	timer3.start()
#定时回调
def TIM3():#显示波形
	global timer3
	global Time_USE
	global Range_USE
	global Show_Data
	global ADC_Data
	global Show_Pag
	global Show_Len
	global PAG_USE
	global LEN_USE
	global HZ_Show
	global HZ_NOW
	global line0_Data
	global line1_Data
	global line2_Data
	global line3_Data
	Time_USE=s1.get()
	Range_USE=s2.get()
	
	len_num = 0#标记当前行数,用于计算
	d_hz = HZ_NOW - HZ_Show #计算相差的行数
	fu_hz = HZ_Show
	if d_hz == 0:#如果无需更新就退出
		timer3=threading.Timer(0.04,TIM3)#25HZ
		timer3.start()
		return
	HZ_Show = HZ_NOW#更新数值
	d_time=int(Time_USE*5)#计算相差的时间单位（Time_USE确保是0.2的整倍数）
	d_poit = d_hz//d_time#计算相差的显示点
	for len_num in range(d_poit):
		fu_page = (fu_hz // LCD_X)% PAG_NUM #获取页
		fu_len = fu_hz % LCD_X #获取行
		fu_hz = fu_hz + d_time#定位到下一位置
		Show_Data[Show_Len][0]=Show_H-int(ADC_Data[fu_page][fu_len][0]*Range_USE)
		Show_Data[Show_Len][1]=Show_H-int(ADC_Data[fu_page][fu_len][1]*Range_USE)
		Show_Data[Show_Len][2]=Show_H-int(ADC_Data[fu_page][fu_len][2]*Range_USE)
		Show_Data[Show_Len][3]=Show_H-int(ADC_Data[fu_page][fu_len][3]*Range_USE)
		Show_Len = Show_Len + 1#指向下一位置
		if(Show_Len==LCD_X):#
			Show_Len=0
	#复制数据
	for value0 in range(LCD_X):
		line0_Data[value0][1]=Show_Data[value0][0]
		line1_Data[value0][1]=Show_Data[value0][1]
		line2_Data[value0][1]=Show_Data[value0][2]
		line3_Data[value0][1]=Show_Data[value0][3]
	
	cv.delete("all")#清除画布所有内容
	#绘制刻度线
	cv.create_line(0,Show_H-0,LCD_X,Show_H-0,fill='green')#0V刻度线
	cv.create_line(0,Show_H-Range_USE*4096*2//5,LCD_X,Show_H-Range_USE*4096*2//5,fill='green')#1V刻度线
	cv.create_line(0,Show_H-Range_USE*4096*4//5,LCD_X,Show_H-Range_USE*4096*4//5,fill='green')#2V刻度线
	
	cv.create_line(LCD_X//2,0,LCD_X//2,Show_H,fill='green')#中间刻度线
	cv.create_line(LCD_X//2+500//(Time_USE*5),0,LCD_X//2+500//(Time_USE*5),Show_H,fill='green')#100ms刻度线
	cv.create_line(LCD_X//2-500//(Time_USE*5),0,LCD_X//2-500//(Time_USE*5),Show_H,fill='green')#100ms刻度线
	
	if Show_Len==0 or Show_Len+SP_NUM >= LCD_X:#这种情况下只用显示一条线
		F_len= (Show_Len+SP_NUM)%LCD_X
		L_len= F_len+LCD_X-SP_NUM
		#为防止显示跟不上,需要
		cv.create_line(line0_Data[F_len:L_len],fill=line0_color)#画直线,可以绘制出折线
		cv.create_line(line1_Data[F_len:L_len],fill=line1_color)#画直线,可以绘制出折线
		cv.create_line(line2_Data[F_len:L_len],fill=line2_color)#画直线,可以绘制出折线
		cv.create_line(line3_Data[F_len:L_len],fill=line3_color)#画直线,可以绘制出折线
	else:#需要显示两条线
		cv.create_line(line0_Data[0:Show_Len+1],fill=line0_color)#画直线,可以绘制出折线
		cv.create_line(line1_Data[0:Show_Len+1],fill=line1_color)#画直线,可以绘制出折线
		cv.create_line(line2_Data[0:Show_Len+1],fill=line2_color)#画直线,可以绘制出折线
		cv.create_line(line3_Data[0:Show_Len+1],fill=line3_color)#画直线,可以绘制出折线
		
		cv.create_line(line0_Data[Show_Len-1+SP_NUM:LCD_X],fill=line0_color)#画直线,可以绘制出折线
		cv.create_line(line1_Data[Show_Len-1+SP_NUM:LCD_X],fill=line1_color)#画直线,可以绘制出折线
		cv.create_line(line2_Data[Show_Len-1+SP_NUM:LCD_X],fill=line2_color)#画直线,可以绘制出折线
		cv.create_line(line3_Data[Show_Len-1+SP_NUM:LCD_X],fill=line3_color)#画直线,可以绘制出折线
	#create_line(, fill='Red', width=2)
	timer3=threading.Timer(0.02,TIM3)#25HZ
	timer3.start()
	
def TIM2():#统计频率
	global timer2
	global HZ_NOW
	global HZ_OLD
	timer2=threading.Timer(1,TIM2)#250HZ
	timer2.start()
	print(str(HZ_NOW-HZ_OLD)+"HZ")
	HZ_OLD=HZ_NOW
	
#定时回调
def TIM1():#接收数据
	global timer1
	global ser
	global ADC_Data
	global PAG_USE
	global LEN_USE
	global HZ_NOW
	len_num = 0#标记当前行数,用于计算
	str_num = 0#标记当前字符数,用于计算
	first_num = 0 
	
	count = ser.inWaiting()#获取串口缓冲区中的数据
	if count !=0 :#如果不用等待接收
		recv = ser.read(ser.in_waiting).decode("gbk")
		first_num = len(recv)#原始数据长度
		#print(recv)
		#做字符处理
		str_num=first_num
		while (str_num >0 ):#直到字符处理完
			while(recv[len_num*26]!='0' or recv[len_num*26+1]!=':'):#如果开头格式不对
				recv = recv[:len_num*26] + recv[len_num*26+1:]#抛弃首位字节
				str_num=str_num-1
			if(recv[len_num*26+7]!=':' or recv[len_num*26+13]!=':' or recv[len_num*26+19]!=':'):#如果后续格式不对
				recv = recv[:len_num*26] + recv[len_num*26+1:]#抛弃首位字节
				str_num=str_num-1
			else :
				len_num=len_num+1
				str_num=str_num-26
		len_num = 0#标记当前行数,用于计算
		str_num = 0#标记当前字符数,用于计算
		HZ_NOW=HZ_NOW+len(recv)//26
		for len_num in range(len(recv)//26):#计数收到了几行数据
			str_num=len_num*26#计算行头位置
			ADC_Data[PAG_USE][LEN_USE][0]=int(recv[str_num+2:str_num+6])
			ADC_Data[PAG_USE][LEN_USE][1]=int(recv[str_num+8:str_num+12])
			ADC_Data[PAG_USE][LEN_USE][2]=int(recv[str_num+14:str_num+18])
			ADC_Data[PAG_USE][LEN_USE][3]=int(recv[str_num+20:str_num+24])
			#换行换页
			LEN_USE=LEN_USE+1#
			if(LEN_USE == LCD_X):
				LEN_USE = 0
				PAG_USE = PAG_USE+1
				if(PAG_USE == PAG_NUM):
					PAG_USE = 0
		#print(ADC_Data)#起始位,末尾位
		#print("PAG="+str(PAG_USE)+"  LEN="+str(LEN_USE))
		#print("All Data="+str(first_num)+"  Get Data"+str((len_num+1)*26))
	timer1=threading.Timer(0.04,TIM1)#25HZ,每次最多只能读取470行
	timer1.start()

timer1=threading.Timer(1,TIM1)#创建定时器,延时为1秒
timer2=threading.Timer(1,TIM2)#创建定时器,延时为1秒
timer3=threading.Timer(1,TIM3)#创建定时器,延时为1秒
#创建主窗口
root = tk.Tk() # 实例化主窗口
root.title("波形探测")#设置标题
root.geometry(str(LCD_X)+"x680+0+0")#主窗口的大小以及在显示器上的位

#创建按键
btn1=tk.Button(root,text="建立连接",height=1,width=8)
btn1.place(x=0,y=620,anchor="w")#设置位置以及对齐方式
btn1.config(command=button1)#连接按键触发事件
#创建画布
cv = Canvas(root,bg = 'white',width=LCD_X,height=Show_H)
#创建滑块条
s1=Scale(root, from_=0.2, to=4,resolution=0.2,label='采样间隔(ms)',orient=HORIZONTAL)#orient=HORIZONTAL 横向，默认纵向
s1.place(x=100,y=640,anchor="w")#W.get()
s2=Scale(root, from_=0.1, to=1,resolution=0.1,label='显示范围调节',orient=HORIZONTAL)#orient=HORIZONTAL 横向，默认纵向
s2.place(x=200,y=640,anchor="w")#W.get()可获取滑块值
#显示画布
cv.place(x=0,y=Show_H//2,anchor="w")
# 进入消息循环
root.mainloop()
